package com.ncr.teradata.bireport;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

/**
 * ReportServer.java
 * 报表服务类
 * 1、包括报表缓存调度进程，根据各报表的属性配置来对报表进行调度，更新数据
 *    （报表配置文件见report-config.xml）
 * 2、根据客户端的报表请求，返回报表数据
 * 3、通过人工触发调度报表，刷新缓存数据
 *
 * @author ylpeng
 * @date Mar 20, 2007
 */
public class ReportServer extends Thread
{
	//报表调度程序线程
	private Thread scheduler;
	
	//报表元数据信息列表
	private List reportMD;
	
	//报表视图元数据
	private List reportViewMD;
	
	//报表配置文件名称（带路径）
	private String reportConfFile;
	
	private static ReportServer instance = new ReportServer();
	public static ReportServer getInstance(){
		return instance;
	}
	
	private ReportServer(){
		reportMD = new ArrayList();
		reportViewMD = new ArrayList();
		
		reportConfFile = this.getClass().getResource("ReportServer.class").getPath();
		reportConfFile = reportConfFile.substring(0,reportConfFile.indexOf("WEB-INF")) + "WEB-INF/classes/"+InitParam.REPORT_CONF_FILE;
		
		buildReportMetaData();
		
		scheduler = new Thread(this);
		scheduler.start();
		
		try{
			//在程序初始化并启动调度线程后暂停10秒钟加载基本数据项
			//Thread.currentThread().sleep(10000);
		}
		catch(Exception e){
			trace("第一次加载报表数据期间发生异常！");
			e.printStackTrace();
		}
	}
	
	/**
	 * 多线程执行方法
	 *
	 * 报表调度程序每隔一定时间会遍历报表元数据，并根据报表属性配置来调度报表缓存
	 */
	public void run(){
		trace("Report Server Started.");
		long sleepDur = 1800000;
		while(true){
			try{
				Date time = new Date();
				if (time.getHours() >= 8 && time.getHours() < 10 )
					sleepDur = 600000;
				else
					sleepDur = 1800000;
				trace("Check Data at " + time.toLocaleString());
				
				//触发对报表配置文件中的所有报表进行调度（只是做触发操作、是否真的需要调度则由报表元数据来进行逻辑判断）
				for (int i = 0; i < reportMD.size(); i++){
					ReportMetaData rmd = (ReportMetaData)reportMD.get(i);
					rmd.doScheduler(time);
				}
				
				//每隔一定时间检查一次缓存中的报表数据是否需要更新
				scheduler.sleep(sleepDur);
			}
			catch(Exception ex){
				trace("报表调度线程发生异常！");
				ex.printStackTrace();
				try{
					scheduler.sleep(sleepDur);
				}
				catch(Exception e){
					//nothing
				}
			}
		}
	}
	
	/**
	 * 得到报表元数据信息列表
	 *
	 *@return List 返回报表元数据列表
	 */
	private List getReportMD(){
		return reportMD;
	}
	
	/**
	 * 得到报表视图元数据
	 *
	 *@return ReportViewMetadata 返回报表视图元数据
	 */
	public ReportViewMetadata getReportViewMetadata(String viewName){
		for (int i = 0; i < reportViewMD.size(); i++){
			ReportViewMetadata rptView = (ReportViewMetadata)reportViewMD.get(i);
			if (rptView.getViewName().equalsIgnoreCase(viewName))
				return rptView;
		}
		return null;
	}
	
	/**
	 * 得到所有被缓存的报表名称
	 * 格式：{rptName1,rptNameCN1,rptName2,rptNameCN2,...,rptNamen,rptNameCNn}
	 *
	 *@return String[] 按预定义的格式返回缓存的报表名称
	 */
	public String[] getCachedReportNames(){
		StringBuffer sb = new StringBuffer();
		for(int i = 0; i < reportMD.size(); i++){
			ReportMetaData rmd = (ReportMetaData)reportMD.get(i);
			if (rmd.getCached().equals("YES")){
				if (i == 0)
					sb.append(rmd.getName());
				else
					sb.append("$" + rmd.getName());
				sb.append("$" + rmd.getNameCN());
			}
		}
		return CommonFunc.division(sb.toString(),"$");
	}
	
	/**
	 * 调度报表
	 *
	 *@param rptName String 报表名称
	 */
	public void doScheduler(String rptName){
		rptName = rptName.toUpperCase().trim();
		for(int i = 0; i < reportMD.size(); i++){
			ReportMetaData rmd = (ReportMetaData)reportMD.get(i);
			if(rmd.getName().equals(rptName)){
				rmd.doScheduler();
				break;
			}
		}
	}
	
	/**
	 * 根据报表名称得到报表数据
	 *
	 *@param rptName 报表名称
	 *@return ReportData 返回报表数据对象
	 */
	public ReportData getReportData(String rptName) {
		rptName = rptName.toUpperCase().trim();
		for (int i = 0; i < reportMD.size(); i++) {
			ReportMetaData rmd = (ReportMetaData)reportMD.get(i);
			if (rmd.getName().equals(rptName)) {
				if (rmd.getCached().equals("YES"))
					return ReportCache.getCachedReportData(rptName);
				else
					return rmd.queryReportData();
			}
		}
		return null;
	}
	
	/**
	 * 读取并解析报表配置文件，生成报表元数据信息
	 */
	public void buildReportMetaData(){
		reportMD.clear();
		reportViewMD.clear();
		try{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	  		factory.setValidating(false);
	    	DocumentBuilder builder = factory.newDocumentBuilder();
	    	Document doc = builder.parse(new File(reportConfFile));
	    	
	    	NodeList reportList = doc.getElementsByTagName("report");
			for(int i = 0;i < reportList.getLength();i++){
				ReportMetaData rmd = new ReportMetaData();
				rmd.setName(doc.getElementsByTagName("report-name").item(i).getFirstChild().getNodeValue().trim());
				rmd.setNameCN(doc.getElementsByTagName("report-name-cn").item(i).getFirstChild().getNodeValue().trim());
				rmd.setType(doc.getElementsByTagName("report-type").item(i).getFirstChild().getNodeValue().trim());
				rmd.setCached(doc.getElementsByTagName("report-cached").item(i).getFirstChild().getNodeValue().trim());
				rmd.setSchedulerTime(doc.getElementsByTagName("report-scheduler-time").item(i).getFirstChild().getNodeValue().trim());
				rmd.setAutoCheck(doc.getElementsByTagName("report-auto-check").item(i).getFirstChild().getNodeValue().trim());
				rmd.setAutoCheckDimension(doc.getElementsByTagName("report-auto-check-dimension").item(i).getFirstChild().getNodeValue().trim());
				rmd.setSql(doc.getElementsByTagName("report-sql").item(i).getFirstChild().getNodeValue().trim());
				reportMD.add(rmd);
			}
			
			NodeList reportViewList = doc.getElementsByTagName("report-view");
			for(int i = 0;i < reportViewList.getLength();i++){
				ReportViewMetadata rptView = new ReportViewMetadata();
				rptView.setViewName(doc.getElementsByTagName("view-name").item(i).getFirstChild().getNodeValue().trim());
				rptView.setViewNameCN(doc.getElementsByTagName("view-name-cn").item(i).getFirstChild().getNodeValue().trim());
				rptView.setViewDesc(doc.getElementsByTagName("view-desc").item(i).getFirstChild().getNodeValue().trim());
				rptView.setOlapUrl(doc.getElementsByTagName("olap-url").item(i).getFirstChild().getNodeValue().trim());
				rptView.setDisplayMode(doc.getElementsByTagName("display-mode").item(i).getFirstChild().getNodeValue().trim());
				rptView.setStyleFile(doc.getElementsByTagName("style-file").item(i).getFirstChild().getNodeValue().trim());
				rptView.setReportName(doc.getElementsByTagName("report-name-rel").item(i).getFirstChild().getNodeValue().trim());
				String filters = doc.getElementsByTagName("filters").item(i).getFirstChild().getNodeValue().trim();
				if (filters.length() > 0){
					String[] filtArray = CommonFunc.division(filters, ";");
					String[][] filt = new String[filtArray.length][];
					for ( int j = 0; j < filtArray.length; j++){
						String dimName = filtArray[j].substring(0,filtArray[j].indexOf("{"));
						String dimValue = filtArray[j].substring(filtArray[j].indexOf("{") + 1,filtArray[j].indexOf("}"));
						filt[j] = CommonFunc.division(dimName + "," + dimValue, ",");
					}
					rptView.setFilters(filt);
				}
				String rowDims = doc.getElementsByTagName("row-dimensions").item(i).getFirstChild().getNodeValue().trim();
				if (rowDims.length() > 0)
					rptView.setRowDimensions(CommonFunc.division(rowDims, ","));
				String colDims = doc.getElementsByTagName("col-dimensions").item(i).getFirstChild().getNodeValue().trim();
				if (colDims.length() > 0)
					rptView.setColDimensions(CommonFunc.division(colDims, ","));
				String pagDims = doc.getElementsByTagName("pag-dimensions").item(i).getFirstChild().getNodeValue().trim();
				if (pagDims.length() > 0)
					rptView.setPagDimensions(CommonFunc.division(pagDims, ","));
				String measures = doc.getElementsByTagName("measures").item(i).getFirstChild().getNodeValue().trim();
				if (measures.length() > 0)
					rptView.setMeasures(CommonFunc.division(measures, ","));
				String dmeasures = doc.getElementsByTagName("derive-measures").item(i).getFirstChild().getNodeValue().trim();
				if (dmeasures.length() > 0){
					String[] dmeaArray = CommonFunc.division(dmeasures, ";");
					String[][] dmea = new String[dmeaArray.length][];
					for ( int j = 0; j < dmeaArray.length; j++){
						String dmeaName = dmeaArray[j].substring(0,dmeaArray[j].indexOf("{"));
						String dmeaPara = dmeaArray[j].substring(dmeaArray[j].indexOf("{") + 1,dmeaArray[j].indexOf("}"));
						dmea[j] = CommonFunc.division(dmeaName + "," + dmeaPara, ",");
					}
					rptView.setDeriveMeasures(dmea);
				}
				String subtotals = doc.getElementsByTagName("subtotals").item(i).getFirstChild().getNodeValue().trim();
				if (subtotals.length() > 0)
					rptView.setSubtotals(CommonFunc.division(subtotals, ","));
				reportViewMD.add(rptView);
			}
		}
		catch(Exception e){
			trace("读取报表配置文件出错，可能报表配置的格式错误或信息不完整！");
			e.printStackTrace();
		}
	}
	
	/**
	 * 向控制台输出消息
	 *
	 *@param msg String 消息内容
	 */
	private void trace(String msg){
		System.out.println("[ReportServer]::" + msg);
	}
}